// https://godbolt.org/z/oaEavj39q

#pragma once

#include <string>
#include <vector>

#include "macros.h"

namespace Common {

  /**
   * @brief 固定容量的对象内存池（Memory Pool）。
   *
   *  - 模板参数 @tparam T 指定池中管理的对象类型。
   *  - 构造时预先分配指定数量的对象槽位（ObjectBlock），每个槽位包含一个 T 实例的存储空间
   *    与一个布尔标记，用于指示当前槽位是否空闲。
   *  - 调用 allocate() 时使用 placement new 在槽位上构造对象；
   *    调用 deallocate() 时仅重置空闲标记，不调用对象析构函数。
   *
   * ⚠️ 设计假设：
   *  - T 必须是 ObjectBlock 的首个数据成员（构造函数会通过 reinterpret_cast 断言该布局）。
   *  - deallocate() 不调用析构函数，适用范围仅限于 POD 或生命周期可忽略析构的类型。
   *  - 内存池容量固定；若耗尽，将触发断言。
   */
  template<typename T>
  class MemPool final {
  public:
    /**
     * @brief 构造固定容量的内存池。
     *
     * @param num_elems 预分配的槽位数量。
     * @note 这里使用 vector<ObjectBlock> 进行存储，以确保动态分配，避免栈上大块内存。
     */
    explicit MemPool(std::size_t num_elems)
        : store_(num_elems, {T(), true}) /* 预先构造 num_elems 个 ObjectBlock，默认标记为空闲。 */
    {
      // 断言：确保 ObjectBlock 内部的 T 对象与结构体起始地址对齐。
      // 若失败，说明 T 不是首个成员，后续将 T* 转换为 ObjectBlock* 的做法不再安全。
      ASSERT(reinterpret_cast<const ObjectBlock *>(&(store_[0].object_)) == &(store_[0]),
             "T object should be first member of ObjectBlock.");
    }

    /**
     * @brief 在内存池中分配一个对象，并调用 T 的构造函数。
     *
     * @tparam Args 完美转发给 T 构造函数的参数类型。
     * @param args  构造 T 所需的参数，以按值传递方式接收。
     * @return 指向新构造对象的指针。
     *
     * @note
     *  - 使用 placement new 在预先分配的内存上构造对象。
     *  - 每次分配都会更新 `next_free_index_` 指向下一个可用槽位。
     */
    template<typename... Args>
    T *allocate(Args... args) noexcept {
      auto obj_block = &(store_[next_free_index_]); // 获取当前空闲槽位
      // 断言：确认当前索引对应的块确实空闲，避免覆盖正在使用的对象。
      ASSERT(obj_block->is_free_, "Expected free ObjectBlock at index:" + std::to_string(next_free_index_));

      T *ret = &(obj_block->object_);      // 获取槽位中 T 的原生指针
      ret = new(ret) T(args...);           // placement new：在已有内存上构造 T
      obj_block->is_free_ = false;         // 标记槽位为占用状态

      updateNextFreeIndex();               // 更新下一个空闲槽位索引
      return ret;
    }

    /**
     * @brief 回收指定指针指向的对象槽位。
     *
     * @param elem 指向池内对象的指针。
     * @note 仅将槽位标记为空闲，不调用对象析构函数；使用者需要自行管理资源释放。
     */
    auto deallocate(const T *elem) noexcept {
      // 将 T* 强制解释为 ObjectBlock*，然后通过指针差值计算索引。
      const auto elem_index = (reinterpret_cast<const ObjectBlock *>(elem) - &store_[0]);

      // 断言：检查待回收指针是否确实来自当前内存池。
      ASSERT(elem_index >= 0 && static_cast<size_t>(elem_index) < store_.size(),
             "Element being deallocated does not belong to this Memory pool.");
      // 断言：确保该槽位当前处于“已占用”状态，防止重复回收导致逻辑错误。
      ASSERT(!store_[elem_index].is_free_,
             "Expected in-use ObjectBlock at index:" + std::to_string(elem_index));

      store_[elem_index].is_free_ = true; // 仅重置空闲标记
    }

    // 禁用默认构造、拷贝/移动构造与赋值，避免无意间复制内存池状态。
    MemPool() = delete;
    MemPool(const MemPool &) = delete;
    MemPool(const MemPool &&) = delete;
    MemPool &operator=(const MemPool &) = delete;
    MemPool &operator=(const MemPool &&) = delete;

  private:
    /**
     * @brief 寻找下一个可用槽位。
     *
     *  - 从当前 `next_free_index_` 开始向前扫描。
     *  - 若到达末尾，则回绕至 0（循环队列思路）。
     *  - 如果完整扫描一圈仍然没有找到空闲槽位，则触发断言，提示池已耗尽。
     *
     * @note 常见路径是“快速命中空闲槽位”，因此配合 `LIKELY/UNLIKELY` 提示编译器进行分支预测优化。
     */
    auto updateNextFreeIndex() noexcept {
      const auto initial_free_index = next_free_index_;
      while (!store_[next_free_index_].is_free_) {
        ++next_free_index_;
        if (UNLIKELY(next_free_index_ == store_.size())) { // 回绕到起始位置（该分支应当很少触发）
          next_free_index_ = 0;
        }
        if (UNLIKELY(initial_free_index == next_free_index_)) {
          // 断言：若重新回到起始索引，说明没有可用槽位，内存池已用尽。
          ASSERT(initial_free_index != next_free_index_, "Memory Pool out of space.");
        }
      }
    }

    /**
     * @brief 内部存储结构：封装 T 对象和空闲标记。
     *
     * 将这两个信息放在一个结构体中有助于缓存局部性，因为分配/释放时通常会同时访问两者。
     */
    struct ObjectBlock {
      T object_;       ///< 预留给对象的存储空间（生命周期由 placement new 控制）
      bool is_free_ = true; ///< 当前槽位是否空闲
    };

    std::vector<ObjectBlock> store_; ///< 内部存储容器，大小在构造时固定
    size_t next_free_index_ = 0;     ///< 下一个候选空闲槽位索引（循环使用）
  };

} // namespace Common